/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2021年6月5日
 */
package com.massyframework.beanskin.runtime.framework.installing.assembly;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import com.massyframework.beanskin.Module;
import com.massyframework.beanskin.ModuleIdentifier;
import com.massyframework.beanskin.ModuleJarsLocator;
import com.massyframework.beanskin.ModuleRepository;
import com.massyframework.beanskin.RequireLibraryDescriptor;
import com.massyframework.beanskin.SimpleModuleIdentifier;
import com.massyframework.beanskin.assembly.Assembly;
import com.massyframework.beanskin.assembly.AssemblyBuilder;
import com.massyframework.beanskin.assembly.ServletModel;
import com.massyframework.beanskin.assembly.beans.BeanContextHandlerFactory;
import com.massyframework.beanskin.framework.Framework;
import com.massyframework.beanskin.runtime.framework.installing.BeanContextModuleParser;
import com.massyframework.beanskin.runtime.framework.installing.ModuleClassLoaderBuilderFactory;
import com.massyframework.beanskin.runtime.framework.installing.SimpleRequireLibraryIntersetor;
import com.massyframework.beanskin.service.DependencyServiceDescriptorBuilder;
import com.massyframework.beanskin.service.ExportServiceDescriptorBuilder;
import com.massyframework.beanskin.service.secure.SecureCapacity;
import com.massyframework.beanskin.util.classloading.LocalResourcesPath;
import com.massyframework.beanskin.util.classloading.ModuleClassLoader;
import com.massyframework.beanskin.util.classloading.ModuleClassLoaderBuilder;
import com.massyframework.beanskin.util.classloading.ResourceLocateFilterRepository;
import com.massyframework.beanskin.util.localstore.LocalStore;
import com.massyframework.beanskin.util.version.VersionRange;

/**
 * 装配件解析器
 * @author huangkh
 *
 */
public class AssemblyXmlParser extends BeanContextModuleParser<AssemblyBuilder> {
	
	private ModuleRepository moduleRepository;

	public AssemblyXmlParser(Assembly kernel, LocalStore localStore, ModuleRepository moduleRepository) {
		super(kernel, localStore);
		this.moduleRepository = Objects.requireNonNull(moduleRepository, "\"moduleRepository\" cannot be null.");
	}

	@Override
	protected void setBeanContext(String beanContextName, String versionRange, BeanContextHandlerFactory factory,
			ServletModel servletMode, AssemblyBuilder builder) {
		builder.beanContext(beanContextName, VersionRange.valueOf(versionRange), factory);
		builder.setServletModel(servletMode);
	}

	@Override
	protected void setConfigLocations(List<String> configLocations, AssemblyBuilder builder) {
		for (String configLocation: configLocations) {
			builder.addConfigLocation(configLocation);
		}
	}

	@Override
	protected void setConfigTypes(List<Class<?>> configTypes, AssemblyBuilder builder) {
		for (Class<?> configType: configTypes) {
			builder.addConfigType(configType);
		}
	}
	
	@Override
	protected void setDecryption(String publicKey, String certificateName, 
			String certificateData, List<String> classNames, List<String> scriptFileNames, AssemblyBuilder builder) {
		builder.decryption(publicKey, certificateName, certificateData, classNames, scriptFileNames);
	}

	@SuppressWarnings("resource")
	@Override
	protected ModuleClassLoader createModuleClassLoader(BeanContextHandlerFactory factory, 
			AssemblyBuilder builder) {
		ModuleJarsLocator moduleJarsLocator = 
				this.getKernel().getAssemblyContext()
					.getBean(ModuleJarsLocator.class.getName(), ModuleJarsLocator.class);
		
		List<URL> urls = moduleJarsLocator.locate(builder.getModuleIdentifier());
				
		//计算依赖的类库
		List<RequireLibraryDescriptor> requires = new ArrayList<>();
		ClassLoader classLoader = factory.getClass().getClassLoader();
		if (classLoader instanceof ModuleClassLoader) {
			ModuleClassLoader mLoader = (ModuleClassLoader)classLoader;
			ModuleIdentifier identifier = SimpleModuleIdentifier.newBuilder()
					.groupId(mLoader.getGroupId())
					.moduleId(mLoader.getModuleId())
					.version(mLoader.getVersion())
					.build();
			Module<?> module = this.moduleRepository.findModuleByIdentifier(identifier);
			requires.addAll(module.getConfig().getRequires());
			
		}
		
		requires.addAll(builder.getRequires());
		
		SimpleRequireLibraryIntersetor intersetor = new SimpleRequireLibraryIntersetor();
		List<RequireLibraryDescriptor> list = intersetor.intersection(
				builder.getModuleIdentifier(), requires, this.moduleRepository);
				
		List<LocalResourcesPath> paths = new ArrayList<>();
		for (RequireLibraryDescriptor require: list) {
            Module<?> module = this.moduleRepository.findModuleWithOptimal(require);
            if (module != null) {
                LocalResourcesPath lrp = module.getModuleClassLoader().getLocalResourcePath();
                if (lrp != null) {
                	paths.add(lrp);
                }
            }
        }
		
		SecureCapacity support = (SecureCapacity)builder;
		ResourceLocateFilterRepository filterRepo =
				this.getKernel().getAssemblyContext().getBean(ResourceLocateFilterRepository.class.getName(), 
						ResourceLocateFilterRepository.class);
		
		ModuleClassLoaderBuilder loaderBuilder =
				ModuleClassLoaderBuilderFactory.createModuleClassLoaderBuilder(this.getKernel());
		ModuleClassLoader result =
				loaderBuilder.publicKey(support.getPublicKey())
					.decryptClassNames(support.getDecryptClassNames())
					.decryptScriptFileNames(support.getDescryptScriptFileNames())
					.certificate(support.getX509Name(), support.getX509Encrypted())
					.parentClassLoader(Framework.class.getClassLoader())
					.moduleIdentifier(builder.getModuleIdentifier())
					.addURLs(urls)
					.addLocalResourcesPaths(paths)
					.resourceLocateFilterRepository(filterRepo)
					.build();
		builder.classLoader(result)
			.moduleRepository(this.moduleRepository);
		
		URL url = result.getResource("BEANSKIN-INF/resources/");
		if (url != null) {
			ResourcesExtractor extractor = new ResourcesExtractor();
			extractor.extract(classLoader, 
					this.getLocalStore().getRuntimeDirectory(this.getModuleIdentifier()).resolve(".resources").toFile());
		}
		return result;
	}

	@Override
	protected void addDependencyService(DependencyServiceDescriptorBuilder descBuilder, AssemblyBuilder builder) {
		builder.addDependencyService(descBuilder.build());
	}

	@Override
	protected void addExportServie(ExportServiceDescriptorBuilder descBuilder, AssemblyBuilder builder) {
		builder.addExportService(descBuilder.build());
	}

	@Override
	protected boolean setParamter(String name, String value, AssemblyBuilder builder) {
		AtomicBoolean result = new AtomicBoolean(true);
		builder.setParameter(name, value, rst -> result.set(rst));
		return result.get();
	}

	@Override
	protected void addRequriedLibrary(RequireLibraryDescriptor descriptor, AssemblyBuilder builder) {
		builder.addRequireLibrary(descriptor);
	}

	@Override
	protected void setModuleIdentifier(ModuleIdentifier identifier, AssemblyBuilder builder) {
		builder.identifier(identifier);
	}

	@Override
	protected void setModuleName(String moduleName, AssemblyBuilder builder) {
		builder.name(moduleName);
	}

	@Override
	protected void setModuleDescription(String description, AssemblyBuilder builder) {
		builder.description(description);
	}
	
	@Override
	protected void setTags(Set<String> tags, AssemblyBuilder builder) {
		builder.addTags(tags);
	}

	@Override
	protected void setDeveloper(String shortName, String name, String homePage, AssemblyBuilder builder) {
		builder.developer(shortName, name, homePage);
	}

	@Override
	protected String getResouceNameWithXmlSchemaFile() {
		return "com/massyframework/beanskin/config/assembly-2.0.0.xsd";
	}

	@Override
	protected Class<AssemblyBuilder> getBuilderType() {
		return AssemblyBuilder.class;
	}

}
